package com.songyang.yougnsql.server;

import com.songyang.yougnsql.common.ErrorMsg;
import com.songyang.yougnsql.common.YoungSQLException;
import com.songyang.yougnsql.config.Config;
import com.songyang.yougnsql.config.ConfigConst;
import com.songyang.yougnsql.core.data.DataManager;
import com.songyang.yougnsql.core.table.TableManager;
import com.songyang.yougnsql.core.transaction.TransactionManager;
import com.songyang.yougnsql.core.version.VersionManagerImpl;
import com.songyang.yougnsql.server.handler.ServerHandler;
import com.songyang.yougnsql.server.serialize.SerializeFactory;
import com.songyang.yougnsql.server.transport.YoungSQLDataPackage;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.apache.commons.cli.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * @ClassName Server
 * @Description
 * @date 2022/12/23 15:32
 * @Author yanceysong
 * @Version 1.0
 */
public class YoungSqlServer {
    public static final String YOUNG_SQL_VERSION = "v1.0 Beta";
    private static final Logger logger = LogManager.getLogger(YoungSqlServer.class);
    private static final long KB = 1 << 10;
    private static final long MB = 1 << 20;
    private static final long GB = 1 << 30;
    private static Integer YOUNG_SQL_SERVER_PORT = 12009;

    public YoungSqlServer() {
        //没有配置文件
        YOUNG_SQL_SERVER_PORT = 12009;
    }

    /**
     * 初始化
     *
     * @param configPath 配置文件路径
     */
    private static void init(String configPath) {
        //初始化配置文件
        Config.initConfig(configPath);
        //初始化序列化工工厂
        SerializeFactory.init();
    }

    public static void main(String[] args) throws InterruptedException, IOException, ParseException {
        Options options = new Options();
        options.addOption("config", true, "config path");
        CommandLineParser parser = new DefaultParser();
        CommandLine cmd = parser.parse(options, args);
        if (cmd.hasOption("config")) {
            init(cmd.getOptionValue("config"));
        }
        try {
            start();
        } catch (YoungSQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 解析配置文件当中的内存
     *
     * @param memStr 配置的字符串
     * @return 对应的字节数
     * @throws YoungSQLException 异常
     */
    private static long parseMem(String memStr) throws YoungSQLException {
        if (memStr == null || "".equals(memStr)) {
            return 3 * MB;
        }
        if (memStr.length() < 2) {
            throw new YoungSQLException(ErrorMsg.INVALID_MEM_EXCEPTION);
        }
        String unit = memStr.substring(memStr.length() - 2);
        long memNum = Long.parseLong(memStr.substring(0, memStr.length() - 2));
        switch (unit) {
            case "KB":
            case "kb":
                return memNum * KB;
            case "MB":
            case "mb":
                return memNum * MB;
            case "GB":
            case "gb":
                return memNum * GB;
            default:
                throw new YoungSQLException(ErrorMsg.INVALID_MEM_EXCEPTION);
        }
    }

    /**
     * 启动数据库
     *
     * @throws IOException          异常
     * @throws InterruptedException 异常
     */
    private static void start() throws IOException, InterruptedException, YoungSQLException {
        TransactionManager tm = TransactionManager.open((String) Config.getConfig(ConfigConst.DB_DATA_PATH));
        DataManager dm = DataManager.open((String) Config.getConfig(ConfigConst.DB_DATA_PATH), parseMem((String) Config.getConfig(ConfigConst.MEMORY)), tm);
        TableManager tbm = TableManager.open((String) Config.getConfig(ConfigConst.DB_DATA_PATH), new VersionManagerImpl(tm, dm), dm);
        Executor.initExecutor(tbm);
        //创建BOOSGroup和WorkerGroup
        //bossGroup只处理连接请求，真正的和客户端的业务处理交给workerGroup
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //创建服务器端启动的对象，配置启动参数
            ServerBootstrap bootstrap = new ServerBootstrap();
            //使用链式编程来进行设置
            bootstrap.group(bossGroup, workerGroup)
                    //设置两个线程组
                    .channel(NioServerSocketChannel.class)//使用nioServerSocketChannel的通道实现
                    .option(ChannelOption.SO_BACKLOG, 128)//设置线程队列的连接个数
                    .childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持活动连接状态
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        //给pipeline设置处理器
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //添加处理器
                            pipeline.addLast((ChannelHandler) new ServerHandler());
                        }
                    });//给我们的workerGroup的eventLoop对应的管道设置处理器
            //启动服务器，绑定一个端口并且同步处理
            ChannelFuture channelFuture = bootstrap.bind(YOUNG_SQL_SERVER_PORT).sync();
            //启动成功以后输出欢迎页面
            System.out.println(getWelcomePackage().getData());
            //阻塞后面的逻辑
            channelFuture.channel().closeFuture().sync();
        } finally {
            //优雅的关闭...
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    /**
     * 获取logo的本文
     *
     * @return logo的文本
     * @throws IOException io异常
     */
    private static String getLogo() throws IOException {
        StringBuilder builder = new StringBuilder();
        InputStream is = YoungSqlServer.class.getClassLoader().getResourceAsStream("logo.txt");
        assert is != null;
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String s = "";
        while ((s = br.readLine()) != null) {
            builder.append(s).append("\n");
        }
        return builder.toString();
    }

    /**
     * 获取欢迎的package
     *
     * @return 包裹
     */
    public static YoungSQLDataPackage getWelcomePackage() throws IOException {
        return new YoungSQLDataPackage(getLogo()
                + getVersion()
                + getPoem()
                + " Welcome to YoungSQL " + YoungSqlServer.YOUNG_SQL_VERSION + "!", null);
    }

    /**
     * 返回打油诗
     *
     * @return 打油诗
     */
    private static String getPoem() {
        return "                  写字楼里写字间,写字间里程序员;\n" +
                "                  程序人员写程序,又拿程序换酒钱.\n" +
                "                  酒醒只在网上坐,酒醉还来网下眠;\n" +
                "                  酒醉酒醒日复日,网上网下年复年.\n" +
                "                  但愿老死电脑间,不愿鞠躬老板前;\n" +
                "                  奔驰宝马贵者趣,公交自行程序员.\n" +
                "                  别人笑我太疯癫,我笑自己命太贱;\n" +
                "                  不见满街漂亮妹,哪个归得程序员?\n\n";
    }

    /**
     * 获得版本信息
     *
     * @return 版本信息
     */
    private static String getVersion() {
        return "  ==========::Young SQL::===========              " + (YOUNG_SQL_VERSION.contains("Beta") ? "" : "     ") + "(" + YOUNG_SQL_VERSION + ")\n";
    }

}
